package com.clp.util.event;

import com.clp.util.AssertUtil;
import com.clp.util.CollectionUtil;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * 事件 发布器
 */
public class EventPublisher<S> implements EventListenerRegister<S> {
    private final ExecutorService executor;
    // Map<事件类型， 该事件的监听器列表>
    @SuppressWarnings("rawtypes")
    private final Map<Class<? extends Event>, List<EventListener>> eventListenerMap = new HashMap<>();

    public EventPublisher(ExecutorService executor) {
        AssertUtil.notNull(executor);
        this.executor = executor;
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public <E extends Event<S>> void publishEvent(@Nullable E event) {
        AssertUtil.notNull(event);
        synchronized (eventListenerMap) {
            List<EventListener> listeners = eventListenerMap.get(event.getClass());
            if (listeners == null) return;
            if (!listeners.isEmpty()) {
                executor.execute(() -> {
                    try {
                        synchronized (eventListenerMap) {
                            listeners.forEach(listener -> listener.onEvent(event));
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                });
            }
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends Event<S>> void addEventListener(@Nullable EventListener<E> listener) {
        AssertUtil.notNull(listener);

        synchronized (eventListenerMap) {
            Class<E> eventClass = listener.eventClass();
            List<EventListener> listeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            listeners.add(listener);
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends Event<S>> void addEventListeners(@Nullable Collection<EventListener<E>> listeners) {
        if (CollectionUtil.isEmpty(listeners)) return;
        AssertUtil.nonNullIncludingElements(listeners);

        synchronized (eventListenerMap) {
            Class<E> eventClass = listeners.iterator().next().eventClass();
            List<EventListener> inListeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            inListeners.addAll(listeners);
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends Event<S>> void removeEventListener(@Nullable EventListener<E> listener) {
        AssertUtil.notNull(listener);

        synchronized (eventListenerMap) {
            Class<E> eventClass = listener.eventClass();
            List<EventListener> listeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            listeners.remove(listener);
        }
    }

    @Override
    @SuppressWarnings({"rawtypes"})
    public <E extends Event<S>> void removeEventListeners(@Nullable Collection<EventListener<E>> listeners) {
        if (CollectionUtil.isEmpty(listeners)) return;
        listeners = listeners.stream().filter(Objects::nonNull).collect(Collectors.toCollection(ArrayList::new));
        if (listeners.isEmpty()) return;

        synchronized (eventListenerMap) {
            Class<E> eventClass = listeners.iterator().next().eventClass();
            List<EventListener> inListeners = eventListenerMap.computeIfAbsent(eventClass, k -> new ArrayList<>());
            inListeners.removeAll(listeners);
        }
    }
}
